package main

import (
	"encoding/json"
	"fmt"
	"reflect"
)

type Server struct {
	ServerName string `json:"serverName"`
	ServerIP   string `json:"serverIP"`
}

type ServerSlice struct {
	Servers []Server `json:"servers"`
}

/*
与struct字段相匹配呢？例如JSON的key是Foo，那么怎么找对应的字段呢？
 首先查找tag含有Foo的可导出的struct字段(首字母大写)
 其次查找字段名是Foo的导出字段
 最后查找类似FOO或者FoO这样的除了首字母之外其他大小写不敏感的导出字段
能够被赋值的字段必须是可导出字段(即首字母大写）。同时JSON解析的时候只会解析能找得到的字段，找不到的字段会被忽略，这样的一个好处是：当你接收到一个很大的JSON数据结构而你却只想获取其中的部分数据的时候，你只需将你想要的数据对应的字段名大写，即可轻松解决这个问题
*/
/*
js, err := NewJson([]byte(`{
	"test": {
		"array": [1, "2", 3],
		"int": 10,
		"float": 5.150,
		"bignum": 9223372036854775807,
		"string": "simplejson",
		"bool": true
	}
}`))

arr, _ := js.Get("test").Get("array").Array()
i, _ := js.Get("test").Get("int").Int()
ms := js.Get("test").Get("string").MustString()
*/
func readJson() {
	var s ServerSlice
	str := `{"servers":[{"serverName":"Shanghai VPN","serverIP":"121.13.18.59"},{"serverName":"Beijing VPN","serverIP":"11.23.18.32"}]}`
	json.Unmarshal([]byte(str), &s)
	fmt.Println(s)

	fmt.Println("处理未知格式的json:")
	b := []byte(`{"Name":"Wednesday","Age":20,"Parents":["Gomez","Green"]}`)
	var f interface{}
	err := json.Unmarshal(b, &f)
	if err != nil {
		fmt.Printf("error: %v\n", err)
		return
	}
	fmt.Println(f)
	fmt.Println(reflect.TypeOf(f))
	m := f.(map[string]interface{})
	fmt.Println("read the map:")
	for k, v := range m {
		switch t := v.(type) {
		case string:
			fmt.Println(k, "is string", t)
		case int:
			fmt.Println(k, "is int", t)
		case float64:
			fmt.Println(k, "is float64", t)
		case []interface{}:
			fmt.Println(k, "is an array:")
			for i, u := range t {
				fmt.Println(i, u)
			}
		default:
			fmt.Println(k, "is of a type I don't known how to handle")
		}
	}
}

/*
针对JSON的输出，我们在定义struct tag的时候需要注意的几点是:
 字段的tag是"-"，那么这个字段不会输出到JSON
 tag中带有自定义名称，那么这个自定义名称会出现在JSON的字段名中，例如上面例子中serverName
 tag中如果带有"omitempty"选项，那么如果该字段值为空，就不会输出到JSON串中
 如果字段类型是bool, string, int, int64等，而tag中带有",string"选项，那么这个字段在输出到JSON的时候会把该字段对应的值转换成JSON字符串

Marshal函数只有在转换成功的时候才会返回数据，在转换的过程中我们需要注意几点：
 JSON对象只支持string作为key，所以要编码一个map，那么必须是map[string]T这种类型(T是Go语言中任意的类型)
 Channel, complex和function是不能被编码成JSON的
 嵌套的数据是不能编码的，不然会让JSON编码进入死循环
 指针在编码的时候会输出指针指向的内容，而空指针会输出null
*/
func writeJson() {
	var s ServerSlice
	s.Servers = append(s.Servers, Server{ServerName: "Shanghai VPN", ServerIP: "12.53.21.5"})
	s.Servers = append(s.Servers, Server{ServerName: "Beijing VPN", ServerIP: "31.22.52.86"})
	b, err := json.Marshal(s)
	if err != nil {
		fmt.Println("json err:", err)
		return
	}
	fmt.Println(string(b))
}

func main() {
	// os.Args 获取运行输入参数
	fmt.Println("read from json:")
	readJson()
	fmt.Println("write to json:")
	writeJson()
}
